/*
 * Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */


#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>

#include <unistd.h>
#include <pwd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

// #include <leveldb/db.h>
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>

#include "sgx_trts.h"
#include "sgx_tcrypto.h"
#include "sgx_error.h"

#include "sgx_urts.h"
#include "App.h"
#include "Enclave_u.h"

#define MAX_PATH FILENAME_MAX
#define MAX_LINES 1000
#define MAX_LINE_LENGTH 256

#define MAX_STRING_LENGTH 256
#define MESSAGE_SIZE 16

using namespace std;
// using namespace leveldb;

/* Global EID shared by multiple threads */
sgx_enclave_id_t global_eid = 0;

typedef struct _sgx_errlist_t {
    sgx_status_t err;
    const char *msg;
    const char *sug; /* Suggestion */
} sgx_errlist_t;

/* Error code returned by sgx_create_enclave */
static sgx_errlist_t sgx_errlist[] = {
    {
        SGX_ERROR_UNEXPECTED,
        "Unexpected error occurred.",
        NULL
    },
    {
        SGX_ERROR_INVALID_PARAMETER,
        "Invalid parameter.",
        NULL
    },
    {
        SGX_ERROR_OUT_OF_MEMORY,
        "Out of memory.",
        NULL
    },
    {
        SGX_ERROR_ENCLAVE_LOST,
        "Power transition occurred.",
        "Please refer to the sample \"PowerTransition\" for details."
    },
    {
        SGX_ERROR_INVALID_ENCLAVE,
        "Invalid enclave image.",
        NULL
    },
    {
        SGX_ERROR_INVALID_ENCLAVE_ID,
        "Invalid enclave identification.",
        NULL
    },
    {
        SGX_ERROR_INVALID_SIGNATURE,
        "Invalid enclave signature.",
        NULL
    },
    {
        SGX_ERROR_OUT_OF_EPC,
        "Out of EPC memory.",
        NULL
    },
    {
        SGX_ERROR_NO_DEVICE,
        "Invalid SGX device.",
        "Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards."
    },
    {
        SGX_ERROR_MEMORY_MAP_CONFLICT,
        "Memory map conflicted.",
        NULL
    },
    {
        SGX_ERROR_INVALID_METADATA,
        "Invalid enclave metadata.",
        NULL
    },
    {
        SGX_ERROR_DEVICE_BUSY,
        "SGX device was busy.",
        NULL
    },
    {
        SGX_ERROR_INVALID_VERSION,
        "Enclave version was invalid.",
        NULL
    },
    {
        SGX_ERROR_INVALID_ATTRIBUTE,
        "Enclave was not authorized.",
        NULL
    },
    {
        SGX_ERROR_ENCLAVE_FILE_ACCESS,
        "Can't open enclave file.",
        NULL
    },
    {
        SGX_ERROR_MEMORY_MAP_FAILURE,
        "Failed to reserve memory for the enclave.",
        NULL
    },
};

/* Check error conditions for loading enclave */
void print_error_message(sgx_status_t ret)
{
    size_t idx = 0;
    size_t ttl = sizeof sgx_errlist/sizeof sgx_errlist[0];

    for (idx = 0; idx < ttl; idx++) {
        if(ret == sgx_errlist[idx].err) {
            if(NULL != sgx_errlist[idx].sug)
                printf("Info: %s\n", sgx_errlist[idx].sug);
            printf("Error: %s\n", sgx_errlist[idx].msg);
            break;
        }
    }
    
    if (idx == ttl)
    	printf("Error code is 0x%X. Please refer to the \"Intel SGX SDK Developer Reference\" for more details.\n", ret);
}

/* Initialize the enclave:
 *   Call sgx_create_enclave to initialize an enclave instance
 */
int initialize_enclave(void)
{
    sgx_status_t ret = SGX_ERROR_UNEXPECTED;
    
    /* Call sgx_create_enclave to initialize an enclave instance */
    /* Debug Support: set 2nd parameter to 1 */
    ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
    if (ret != SGX_SUCCESS) {
        print_error_message(ret);
        return -1;
    }

    return 0;
}

/* OCall functions */
void ocall_print_string(const char *str)
{
    /* Proxy/Bridge will check the length and null-terminate 
     * the input string to prevent buffer overflow. 
     */
    printf("%s", str);
}

// const char* readDB(DB* db, string key)
// {
//     string value;
//     Status status = db->Get(leveldb::ReadOptions(), key, &value);
//     if (!status.ok()) {
//         cerr << "Get failed: " << status.ToString() << endl;
//     }
//     cout << "Read from database: key = " << key << ", value = " << value << endl;

//     const char* data = (key+value).c_str();
//     sgx_status_t ret = ecall_read_db(global_eid, data, strlen(data)+1);
//     if (ret != SGX_SUCCESS) {
//         printf("Failed to read db.\n");
//     }

//     return value.c_str();
// }

// void writeDB(DB* db, string key, string value)
// {
//     Status status = db->Put(leveldb::WriteOptions(), key, value);
//     if (!status.ok()) {
//         cerr << "Put failed: " << status.ToString() << endl;
//     }

//     const char* data = (key+value).c_str();
//     sgx_status_t ret = ecall_write_db(global_eid, data, strlen(data)+1);
//     if (ret != SGX_SUCCESS) {
//         printf("Failed to write db.\n");
//     }
// }

// void deleteDB(DB* db, string key)
// {
//     string value;
//     Status status = db->Get(leveldb::ReadOptions(), key, &value);
//     if (!status.ok()) {
//         cerr << "Get failed: " << status.ToString() << endl;
//     }

//     status = db->Delete(leveldb::WriteOptions(), key);
//     if (!status.ok()) {
//         cerr << "delete failed: " << status.ToString() << endl;
//     }

//     const char* data = (key+value).c_str();
//     sgx_status_t ret = ecall_delete_db(global_eid, data, strlen(data)+1);
//     if (ret != SGX_SUCCESS) {
//         printf("Failed to delete db.\n");
//     }
// }

// bool verify(DB* db)
// {
//     ReadOptions read_options;
//     Iterator* it = db->NewIterator(read_options);

//     sgx_status_t ret;
//     string key, value, data;

//     for (it->SeekToFirst(); it->Valid(); it->Next()) {
//         // cout << "Key: " << it->key().ToString() << ", Value: " << it->value().ToString() << endl;
//         cout << "Key: " << it->key().ToString() << ", Value: " << it->value().ToString() << endl;

//         key = it->key().ToString();
//         value = it->value().ToString();
//         data = key+value;

//         ret = ecall_read_verify(global_eid, data.c_str(), strlen(data.c_str())+1);
//         if (ret != SGX_SUCCESS) {
//             printf("Failed to update result\n");
//             break;
//         }
//     }

//     if (!it->status().ok()) {
//         cerr << "Iterator failed: " << it->status().ToString() << endl;
//     }

//     delete it;

//     int res = 0;
//     ecall_verify(global_eid, &res);

//     return res;
// }


// 定义最大SQL语句长度
#define MAX_SQL_LENGTH 1000
#define MAX_RECORD_LENGTH 500

MYSQL *conn;
MYSQL_RES *res1, *res2;
MYSQL_ROW row;
MYSQL_FIELD *field;

void ocall_outer_loop(const char* sql_statement, int* row_num, char* fields, size_t len) {

    // 执行SQL查询
    if (mysql_query(conn, sql_statement)) {
        fprintf(stderr, "Error executing SQL query: %s\n", mysql_error(conn));
        mysql_close(conn);
        return;
    }

    // 获取查询结果
    res1 = mysql_store_result(conn);
    *row_num = mysql_num_rows(res1);

    char temp[MAX_RECORD_LENGTH] = "";
    while ((field = mysql_fetch_field(res1))) {
        strcat(temp, field->name);
        strcat(temp, "|");
    }
    strcpy(fields, temp);
    // printf("%s\n", fields);

    return;
}

void ocall_inner_loop(const char* sql_statement, int* row_num, char* fields, size_t len) {

    // 执行SQL查询
    if (mysql_query(conn, sql_statement)) {
        fprintf(stderr, "Error executing SQL query: %s\n", mysql_error(conn));
        mysql_close(conn);
        return;
    }

    // 获取查询结果
    res2 = mysql_store_result(conn);
    *row_num = mysql_num_rows(res2);

    char temp[MAX_RECORD_LENGTH] = "";
    while ((field = mysql_fetch_field(res2))) {
        strcat(temp, field->name);
        strcat(temp, "|");
    }
    strcpy(fields, temp);

    return;
}

void enclave_get_outer_record(int rid, char* record, size_t len) 
{

    mysql_data_seek(res1, rid);    // 获取特定行的数据
    row = mysql_fetch_row(res1);    // 数据库中的NULL会被返回成一个null指针，会导致报错，目前还没修改

    // 拼接行数据为一个字符串
    char resultString[MAX_RECORD_LENGTH] = "";  // 适当设置缓冲区大小
    for (int i = 0; i < mysql_num_fields(res1); i++) {
        strcat(resultString, row[i]);
        strcat(resultString, "|");  // 在列数据之间添加分隔符
    }

    strcpy(record, resultString);

}

void enclave_get_inner_record(int rid, char* record, size_t len) 
{

    mysql_data_seek(res2, rid);    // 获取特定行的数据
    row = mysql_fetch_row(res2);

    // 拼接行数据为一个字符串
    char resultString[MAX_RECORD_LENGTH] = "";  // 适当设置缓冲区大小
    for (int i = 0; i < mysql_num_fields(res2); i++) {
        strcat(resultString, row[i]);
        strcat(resultString, "|");  // 在列数据之间添加分隔符
    }

    strcpy(record, resultString);

}

void ocall_get_string(char* str, size_t len) {
    // 在这里实现获取字符串的逻辑，将结果写入str中
    // 这里使用简单的示例字符串，实际应该是通过文件、网络等方式获取字符串
    const char* example_string = "Hello from ocall!";
    
    // 复制字符串到传入的缓冲区
    strcpy(str, example_string);
    // snprintf(str, len, "%s", example_string);
}

void print_jsonnode(JsonNode* node) {
    if(node == NULL) {
        return;
    }
    print_jsonnode(node->left);

    printf("Jsonnode type:%s\n", node->nodeType);
    if(!strcmp(node->nodeType, "join")) {
        printf("filter:%s\n", node->joinFilter);
        for(int i=0; i<node->outputNum; i++) {
            printf("output[%d]:%s\n", i, node->output[i]);
        }
    } else if(!strcmp(node->nodeType, "scan")) {
        printf("relation name:%s\n", node->relName);
        printf("alias:%s\n", node->alias);
        for(int i=0; i<node->outputNum; i++) {
            printf("output[%d]:%s\n", i, node->output[i]);
        }
    }
    
    print_jsonnode(node->right);
}

void process_string(string &str) {
    // 找到"::"的位置
    size_t pos = str.find("::");
    if (pos != string::npos) {
        // 找到"::"后的空格位置
        size_t pos2 = str.find(" ", pos);
        if (pos2 != string::npos) {
            // 删除"::"之前和之后的内容
            str.erase(pos, pos2 - pos);
        } else {
            // 如果没有空格，直接删除"::"及之后的内容
            str.erase(pos);
        }
    }
}

void process_node(const Json::Value& node, JsonNode** jsonnode) {
    const string node_type = node["Node Type"].asString();
    // std::cout << node_type << "|" << node.size() << std::endl; 

    if (node_type == "Nested Loop") {
        if(*jsonnode == NULL) {
            *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
            (*jsonnode)->left = NULL;
            (*jsonnode)->right = NULL;
        }
        strcpy((*jsonnode)->nodeType, "join");
        strcpy((*jsonnode)->joinFilter, node["Join Filter"].asCString());
        strcpy((*jsonnode)->alias, "");
        const Json::Value& outputs = node["Output"];
        for(int i=0; i<outputs.size(); i++) {
            const char* dot_ptr = strchr(outputs[i].asCString(), '.');
            strcpy((*jsonnode)->output[i], dot_ptr+1);
        }
        (*jsonnode)->outputNum = outputs.size();
        const Json::Value& plans = node["Plans"];
        process_node(plans[0], &((*jsonnode)->left));
        process_node(plans[1], &((*jsonnode)->right));
    } else if (node_type == "Materialize") {
        const Json::Value& plans = node["Plans"];
        for (const auto& plan : plans) {
            process_node(plan, jsonnode);
        }
    } else if (node_type == "Seq Scan") {
        if(*jsonnode == NULL) {
            *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
            (*jsonnode)->left = NULL;
            (*jsonnode)->right = NULL;
        }
        strcpy((*jsonnode)->nodeType, "scan");
        strcpy((*jsonnode)->relName, node["Relation Name"].asCString());
        string tmp = node["Filter"].asString();
        process_string(tmp);
        cout << tmp << endl;
        strcpy((*jsonnode)->joinFilter, tmp.c_str());
        strcpy((*jsonnode)->sqlStatement, "");
        if(node["Alias"].isNull()) {
            strcpy((*jsonnode)->alias, "");
        } else {
            strcpy((*jsonnode)->alias, node["Alias"].asCString());
        }
        const Json::Value& outputs = node["Output"];
        for(int i=0; i<outputs.size(); i++) {
            // strcpy((*jsonnode)->output[i], outputs[i].asCString());
            const char* dot_ptr = strchr(outputs[i].asCString(), '.');
            strcpy((*jsonnode)->output[i], dot_ptr+1);
        }
        (*jsonnode)->outputNum = outputs.size();
    } else if (node_type == "Sort") {
        if(*jsonnode == NULL) {
            *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
            (*jsonnode)->left = NULL;
            (*jsonnode)->right = NULL;
        }
        strcpy((*jsonnode)->nodeType, "sort");
        const Json::Value& sortkeys = node["Sort Key"];
        int i;
        for(i=0; i<sortkeys.size(); i++) {
            strcpy((*jsonnode)->sortKey[i], sortkeys[i].asCString());
        }
        (*jsonnode)->sortKeyNum = i;
        printf("sortKeyNUM:%d\n", i);
        const Json::Value& plans = node["Plans"];
        process_node(plans[0], &((*jsonnode)->left));
    } else if (node_type == "Aggregate") {
        if(*jsonnode == NULL) {
            *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
            (*jsonnode)->left = NULL;
            (*jsonnode)->right = NULL;
        }
        strcpy((*jsonnode)->nodeType, "aggregate");
        const Json::Value& outputs = node["Output"];
        int i;
        for(i=0; i<outputs.size(); i++) {
            strcpy((*jsonnode)->output[i], outputs[i].asCString());
        }
        (*jsonnode)->outputNum = i;
        const Json::Value& groupkeys = node["Group Key"];
        int j;
        for(j=0; j<groupkeys.size(); j++) {
            strcpy((*jsonnode)->groupKey[j], groupkeys[j].asCString());
        }
        (*jsonnode)->groupKeyNum = j;
        // printf("groupKeyNUM:%d\n", j);
        const Json::Value& plans = node["Plans"];
        process_node(plans[0], &((*jsonnode)->left));
    }
}

/* Application entry */
int SGX_CDECL main(int argc, char *argv[])
{
    (void)(argc);
    (void)(argv);

    /* Initialize the enclave */
    if(initialize_enclave() < 0){
        printf("Enter a character before exit ...\n");
        getchar();
        return -1; 
    }

    // /* Utilize edger8r attributes */
    // edger8r_array_attributes();
    // edger8r_pointer_attributes();
    // edger8r_type_attributes();
    // edger8r_function_attributes();
    
    // /* Utilize trusted libraries */
    // ecall_libc_functions();
    // ecall_libcxx_functions();
    // ecall_thread_functions();

    /* my code */

    // std::ifstream json_file("plan.json");
    // if (!json_file.is_open()) {
    //     std::cerr << "无法打开JSON文件" << std::endl;
    //     return 1;
    // }

    // Json::CharReaderBuilder reader;
    // Json::Value root;
    // Json::parseFromStream(reader, json_file, &root, nullptr);

    // JsonNode *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
    // jsonnode->left = NULL;
    // jsonnode->right = NULL;

    // const Json::Value& query_plan = root["QUERY PLAN"];

    // for (const auto& plan : query_plan) {
    //     process_node(plan["Plan"], &jsonnode);
    // }

    // std::ifstream json_file("queryplan_q3.json");
    std::ifstream json_file("q3_test.json");
    if (!json_file.is_open()) {
        std::cerr << "无法打开JSON文件" << std::endl;
        return 1;
    }

    Json::CharReaderBuilder reader;
    Json::Value root;
    Json::parseFromStream(reader, json_file, &root, nullptr);

    JsonNode *jsonnode = (JsonNode*)malloc(sizeof(JsonNode));
    jsonnode->left = NULL;
    jsonnode->right = NULL;

    const Json::Value& query_plan = root;

    for (const auto& plan : query_plan) {
        process_node(plan["Plan"], &jsonnode);
    }

    // print_jsonnode(jsonnode);

    clock_t start = clock();

    // 初始化MySQL连接
    conn = mysql_init(NULL);

    // 连接到数据库
    if (mysql_real_connect(conn, "localhost", "xmh", "0506", "tpch", 0, NULL, 0) == NULL) {
        fprintf(stderr, "Error connecting to MySQL: %s\n", mysql_error(conn));
        return 1;
    }

    enclave_generate_sql(global_eid, jsonnode, sizeof(*jsonnode));

    clock_t end = clock();

    cout << "time: " << ((double)(end-start))/CLOCKS_PER_SEC << endl;

    // 释放结果集
    mysql_free_result(res1);
    mysql_free_result(res2);

    // 关闭连接
    mysql_close(conn);

    /* Destroy the enclave */
    sgx_destroy_enclave(global_eid);
    
    printf("Info: SampleEnclave successfully returned.\n");

    // printf("Enter a character before exit ...\n");
    // getchar();
    return 0;
}

